//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <winhack.h>
#include <elasys_server.h>
#include <driver.h>
#include <stdio.h>
#include <stdlib.h>
#include <bootmods.h>

_ELASTOS Boolean AddDefaultPath(/* out */ wchar_t *pwszFileName)
{
    wchar_t *szFilePath;
    wchar_t szTempName[_MAX_PATH];

    wcscpy(szTempName, pwszFileName);

    // Get the full path of the config file
    szFilePath = _wgetenv(L"XDK_SIMULATOR_RESOURCE");
    if (szFilePath) {
        // The file is in the special config directory
        //wprintf(L"in AddDefaultPath EMULATOR_RESOURCE %s\n", szFilePath);
        wcscpy(pwszFileName, szFilePath);
    }
    else {
        // Think the file is in the current directory
        wcscpy(pwszFileName, szTempName);
        return TRUE;
    }
    wcscat(pwszFileName, L"\\");
    wcscat(pwszFileName, szTempName);
    assert(wcslen(pwszFileName) < _MAX_PATH);

    return TRUE;
}

_ELASTOS Boolean AddAppendPath(/* out */ wchar_t *pwszFileName, wchar_t *pwszEnvName, wchar_t *pwszAppendDir)
{
    wchar_t *szFilePath;
    wchar_t szTempName[_MAX_PATH];

    wcscpy(szTempName, pwszFileName);

    // Get the full path of the config file
    szFilePath = _wgetenv(pwszEnvName);
    if (szFilePath) {
        wcscpy(pwszFileName, szFilePath);
        wcscat(pwszFileName, L"\\");
        wcscat(pwszFileName, pwszAppendDir);
    }
    else {
        // Think the file is in the current directory
        wcscpy(pwszFileName, szTempName);
        return TRUE;
    }
    wcscat(pwszFileName, L"\\");
    wcscat(pwszFileName, szTempName);
    assert(wcslen(pwszFileName) < _MAX_PATH);

    return TRUE;
}

// Boot Modules
const uint_t c_uMaxLengthOfBootModuleName = 19;

typedef struct BootModule {
    char            szName[c_uMaxLengthOfBootModuleName + 1];
    uint_t          size;
    //MemoryRegion    region;
    void            *pFile;
} BootModule;

BootModule                     *g_pBootModules;
uint_t                         g_uNumberOfBootModules;

class BootModules : public CDeviceDriver
{
public:
    CARAPI Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose();

public:
    BootModules() { m_nCurrentIndex = -1; }

    ECode Init();
    ECode Reset();
    ECode MoveNext();
    ECode Current(BMFileInfo *);
    ECode Delete(UInt32 uIndex);
    ECode Find(char *pszName, UInt32 *pIndex);
    ECode GetInfo(char *pszName, BMFileInfo *pInfo);

public:
    Int32         m_nCurrentIndex;
};

#define INVALID_CURRENT_POS     -2

#define BOOT_MODULE_SIZE(m)     (m).size

ECode BootModules::Init()
{
    wchar_t wszFileName[_MAX_PATH];
    FILE *fp = NULL;
    for (int i = 0; i < (Int32)g_uNumberOfBootModules; i++) {
        mbstowcs(wszFileName, g_pBootModules[i].szName, _MAX_PATH);
        AddDefaultPath(wszFileName);

        fp = _wfopen(wszFileName, L"rb");
        if (NULL == fp) {
            mbstowcs(wszFileName, g_pBootModules[i].szName, _MAX_PATH);
            AddAppendPath(wszFileName, L"XDK_COMMON_RESOURCES", L"i18n");
            //printf("wszFileName:%S.\n", wszFileName);
            fp = _wfopen(wszFileName, L"rb");
        }
        assert(fp);
        fseek(fp, 0, SEEK_END);
        g_pBootModules[i].size = ftell(fp);
        g_pBootModules[i].pFile = malloc(g_pBootModules[i].size);
        assert(g_pBootModules[i].pFile);
        fseek(fp, 0, SEEK_SET);
        fread(g_pBootModules[i].pFile, 1, g_pBootModules[i].size, fp);
        fclose(fp);
    }

    return NOERROR;
}

ECode BootModules::Read(
    /* [in] */ Int64 u64Offset,
    /* [in] */ Int32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent **ppCompletionEvent)
{
    return E_ACCESS_DENIED;
}

ECode BootModules::Write(
    /* [in] */ Int64 u64Offset,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ Int32 * pBytesWritten,
    /* [out] */ IEvent **ppCompletionEvent)
{
    return E_ACCESS_DENIED;
}

ECode BootModules::Reset()
{
    m_nCurrentIndex = -1;
    return NOERROR;
}

ECode BootModules::MoveNext()
{
    if (INVALID_CURRENT_POS == m_nCurrentIndex) {
        return E_INVALID_OPERATION;
    }
    m_nCurrentIndex++;

    while (m_nCurrentIndex < (Int32)g_uNumberOfBootModules
        && '\0' == g_pBootModules[m_nCurrentIndex].szName[0]) {
        m_nCurrentIndex++;
    }
    if (m_nCurrentIndex >= (Int32)g_uNumberOfBootModules) {
        m_nCurrentIndex = INVALID_CURRENT_POS;
        return S_FALSE;
    }

    return NOERROR;
}

ECode BootModules::Current(BMFileInfo *pInfo)
{
    if (INVALID_CURRENT_POS == m_nCurrentIndex || -1 == m_nCurrentIndex) {
        return E_INVALID_OPERATION;
    }
    if ('\0' == g_pBootModules[m_nCurrentIndex].szName[0]) {
        return E_INVALID_OPERATION;
    }

    pInfo->uIndex = (UInt32)m_nCurrentIndex;
    pInfo->uUAddr = (UInt32)g_pBootModules[m_nCurrentIndex].pFile;
    pInfo->uSize = g_pBootModules[m_nCurrentIndex].size;
    strcpy(pInfo->szName, g_pBootModules[m_nCurrentIndex].szName);

    return NOERROR;
}

ECode BootModules::Delete(UInt32 uIndex)
{
    return E_NOT_IMPLEMENTED;
}

ECode BootModules::Find(char *pszName, UInt32 *pIndex)
{
    if (pszName == NULL || '\0' == *pszName) {
        return E_INVALID_ARGUMENT;
    }

    for (int i = 0; i < (Int32)g_uNumberOfBootModules; i++) {
        if (_stricmp(pszName, g_pBootModules[i].szName) == 0) {
            *pIndex = i;
            return NOERROR;
        }
    }

    return E_FILE_NOT_FOUND;
}

ECode BootModules::GetInfo(char *pszName, BMFileInfo *pInfo)
{
    UInt32 uIndex;
    ECode ec = Find(pszName, &uIndex);
    if (FAILED(ec)) return ec;

    pInfo->uIndex = uIndex;
    pInfo->uUAddr = (UInt32)g_pBootModules[uIndex].pFile;
    pInfo->uSize = g_pBootModules[uIndex].size;

    return NOERROR;
}

ECode BootModules::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf & inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    switch (nControlCode) {
        case BMCommand_Reset:
            return this->Reset();

        case BMCommand_MoveNext:
            return this->MoveNext();

        case BMCommand_Current:
            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            pOutBuffer->SetUsed(pOutBuffer->GetCapacity());
            return this->Current((BMFileInfo *)pOutBuffer->GetPayload());

        case BMCommand_Delete:
            return this->Delete(*(UInt32 *)inBuffer.GetPayload());

        case BMCommand_Open:
            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            pOutBuffer->SetUsed(12);
            //asm("int $3");
            return this->GetInfo((char *)inBuffer.GetPayload(), (BMFileInfo *)pOutBuffer->GetPayload());
        case BMCommand_Close:
            return NOERROR;

        default:
            return E_INVALID_OPTIONS;
    }
}

void BootModules::Dispose()
{
    for (int i = 0; i < (Int32)g_uNumberOfBootModules; i++) {
        if (g_pBootModules[i].pFile) {
            free(g_pBootModules[i].pFile);
        }
    }
    delete this;
}

static BootModule ModuleList[] = {
    {"services.cfg"},
    {"elastos.cfg"},
    {"encoding.dat"},
    };

EXTERN_C IDeviceDriver * CDECL CreateBootModules(uint_t uDeviceNo, void *pvParameter)
{
    g_pBootModules = ModuleList;
    g_uNumberOfBootModules = sizeof(ModuleList) / sizeof(BootModule);
//    printf("g_uNumberOfBootModules %d\n", g_uNumberOfBootModules);

    BootModules *pBootModules = new BootModules();
    if (NULL == pBootModules) return NULL;

    if (FAILED(pBootModules->Init())) return NULL;

    pBootModules->AddRef();
    return pBootModules;
}
